"""
    Contains CRUDController, ItemTab and ItemActionsTab.
"""
import sys
import copy
import turbogears

from turbogears import error_handler, exception_handler, validate, expose, database, identity, controllers, redirect
from cherrypy._cperror import NotFound
from cherrypy import request
from genshi.template.plugin import ET
import sqlobject

from mnemoply._renewed_lib.containers import ModelObjectContainer
from mnemoply._renewed_lib.lookup import LookupController, TableCol
from mnemoply._renewed_lib.widgets_columns import column_item_view, column_widget
from mnemoply._renewed_lib.widgets import TableForm, Widget, HiddenField
from mnemoply._renewed_utils import get_referer, get_by_key_and_delete, get_host_url
from mnemoply._renewed_common_classes import Wrapper
from mnemoply._renewed_model.columns import NoDefault
from mnemoply._renewed_lib.validators import EmptyStringConverter
from mnemoply._renewed_lib.actions import Action, ParamsColumn

class ItemTab(controllers.Controller):
    """
        Represent small tab lookup inside other controller.
    """

    params = ['title']
    title = "tab"

    def update_params(self, d):
        super(ItemTab, self).update_params(d)
        d['tab'] = self

    def display(self):
        return None, None

class ItemActionsTab(ItemTab):
    """
        Represent small tab lookup inside other controller with mass actions.
    """

    params = ['ma_class_name']

    title = "Actions tab"
    translated = [_("No records"), _("Select all"), _("Clear all")]
    template = "item_actions_tab"
    ma_class_name = None
    name = None

    def display(self):
        #TODO
        return self.ma_class_name, self.name

    def getRecords(self, obj):
        return []

    def getRecordTitle(self, record):
        return record.title

class ItemActionsTab1(ItemTab):
    """
        Represent small tab lookup inside other controller with mass actions.
    """

    title = "Actions tab"
    template = "item_actions_tab1"
    name = None
    fields = []
    obj_field = None
    model_object_child=None
    field_object_main=None
    
    def getRecords(self, obj):
        return []
    @expose()
    def delete_action(self, obj, _id):
        self.model_object_child.get(int(_id)).destroySelf()
        raise redirect("./")
    def add_action(self, obj, **kw):
        pass
    def get_add_form(self, obj=None):
        add_form = TableForm(
            fields=self.fields,
            submit_text=_("Add"),
            action='add_action',
        )
        return add_form
    def getRecordTitle(self, record):
        return record.title

class RestrictController(ModelObjectContainer, LookupController):
    form_widget_class = TableForm
    item_template = "genshi:mnemoply._renewed_templates.crud.item"
    view_fields = None
    model_object = None
    item_tabs = []
    item_tabs1 = []
    title = ''
    actions=[]
    def __init__(self):
        ModelObjectContainer.__init__(self, self.model_object)
        if self.mysql_view_name is None or not self.mysql_view_name:
            self.mysql_view_name = self.model_object.sqlmeta.table+'_lookup'
        LookupController.__init__(self,
            columns=self.lookup_columns,
            mass_actions_class=self.model_object,
            title=self.title or self.model_object.model_title,
        )
        if self.view_fields is None:
            self.view_fields = self.model_object.sqlmeta.columns.keys()
        self.is_showable = len(self.view_fields)
        
        #class ac(object):
        #    def __init__(self, name, is_s = False, is_m = False ):
        #        self.name=name
        #        self.is_s = is_s
        #        self.is_m = is_m
        #lst = [ac(1),ac(2),ac(3,True),ac(4,False,True)]
        #lst1 = [a for a in lst if not a.is_s]
        #lst2 = [a for a in lst if not a.is_m]
        #n = [a.name for a in lst]
        #n1 = [a.name for a in lst1]
        #n2 = [a.name for a in lst2]
        #print 'lst ', lst, n
        #print 'lst1 ', lst1, n1
        #print 'lst2 ', lst2, n2
        #    
        
        #self.facets.extend(self.actions)
        #for action in self.actions:
        #    action.controller = self
        #    setattr(self, action.name, action.show_params)

        self.facets.extend(self.item_tabs)

        for item in self.item_tabs:
            item.controller = self
    
        if self.actions:
            for action in self.actions:
                if not action.model_object:
                    action.model_object = self.model_object;
    @expose(format="json")
    def find(self, obj=None, searchString='', **kw):
        """
        
        """
        searchString = searchString.lower().strip()
        
        model_object = kw.get('model_object') or self.search_model or self.model_object
        obj_condition = kw.get('obj_condition',lambda obj: obj.title.lower().strip())
        exposed_method = kw.get('exposed_method', lambda obj: obj.title)
        where_condition = kw.get('where_condition',[])
        pre_obj_condition=kw.get('pre_obj_condition', lambda(obj):True)
        use_filter = kw.get('use_filter', True)
        if use_filter and self.filter_by_user:
            if issubclass(type( where_condition),str):
                where_condition = [where_condition]
            flt = [self.get_filter_by_user()]
            where_condition +=flt
        if issubclass( type(where_condition),list):
            where_condition = ' AND '.join(where_condition)
        
        if not where_condition:
            where_condition = None
            
        items = []
        sel = list(model_object.select(where_condition))
        if len(searchString) > 2:
            for obj in sel:
                if  pre_obj_condition(obj) and (obj_condition(obj).count(searchString) or str(obj.id) == searchString):
                    items.append((exposed_method(obj), obj.id))
            items.sort()
        print len(items)
        return dict(textItems=items)

    @expose()
    def get_info(self,**kw):
        return dict(result='')
        get_info.expose_object = True
    
    def obj2item_view(self, obj):
        values = database.so_to_dict(obj)
        result = []
        for field in self.view_fields:
            k = field
            v = values[k]
            view = column_item_view(obj.sqlmeta.columns[k], v)
            if isinstance(view,Widget):
                view = ET(view.display())
            result += [ dict(value=view, label=obj.sqlmeta.columns[k].title)]        
        return result

    @expose()
    def show(self, obj):
        state = 'show'
        local_menu=self.create_menu(state, False)
        return dict(
            obj=obj,
            local_menu=local_menu,
            controller=self,
            values=self.obj2item_view(obj),
            tabs=self.item_tabs,
            tg_template=self.item_template,
        )
    show.expose_object = True
    
class CRUDController(RestrictController):
    """
        Based on FastData controller. Create view and edit form from model.
    """

    form_template = "genshi:mnemoply._renewed_templates.crud.form"
    add_fields = None
    edit_fields = None
    redirect_after_edit = "."
    redirect_after_add = "."
    is_deleteable = True
    widget_list = {}
    edit_validators=None
    add_validators=None
    chained_validators_edit=None
    chained_validators_add=None
    mass_actions = None
    single_actions = None

    def __init__(self):
        RestrictController.__init__(self)

        if self.edit_fields is None:
            self.edit_fields = self.view_fields
        self.is_editable = len(self.edit_fields)
        if self.add_fields is None:
            self.add_fields = self.edit_fields
        self.is_addable = len(self.add_fields)
        
        if self.chained_validators_edit is None:
            self.chained_validators_edit=[]
        if self.chained_validators_add is None:
            self.chained_validators_add=self.chained_validators_edit
        fields, self.add_validators = self.fields_for(self.model_object, self.add_fields)
        self.add_form = self.form_widget_class(fields=fields)
        #if self.get_validator_add():
        #    self.add_form.validator.add_chained_validator(self.get_validator_add())
        
        fields, self.edit_validators = self.fields_for(self.model_object, self.edit_fields)
        self.edit_form = self.form_widget_class(fields=fields)
        for v in self.chained_validators_edit:
            self.edit_form.validator.add_chained_validator(v)
        for v in self.chained_validators_add:
            self.add_form.validator.add_chained_validator(v)
        if self.actions:
            for action in self.actions:
                if not action.model_object:
                    action.model_object = self.model_object;
        #if self.single_actions:
        #    for action in self.single_actions:
        #        if not action.model_object:
        #            action.model_object = self.model_object;            

    def obj2edit_view(self, obj):
        return database.so_to_dict(obj)

    def _get_add_form(self):
        return self.add_form

    def _get_edit_form(self):
        return self.edit_form
    
    def redirect_after(self, came_from=None, obj=None):
        referer = came_from or get_referer()
        idd = obj.id if obj else ''
        if referer == get_host_url():
            return "/%s/%s"%(self.controller_name, idd)
        if '/' in referer:
            return referer[:referer.rfind("/")]
    def before_update_edit(self, data, obj):
        return data
    def before_update_add(self, data):
        return data

    def before_update(self, data, obj=None):
        if obj:
            data = self.before_update_edit(data, obj)
        else:
            data = self.before_update_add(data)
        owner = identity.current.user.person
        return self.model_object.addOwnerToData(data, owner)
    def after_update(self, obj=None, **data):
        came_from = data.get('came_from', self.controller_name)
        print 'after_update ', came_from
        raise turbogears.redirect(came_from)
        

    def _update_kw(self, obj, kw):
        return kw

    """
    next 3 functions are used in edit_req for readability
    if obj is not None in this functions that means we are editing,
    if obj is None - adding
    """
    def _prepare_edit(self, obj=None):
        if obj:
            state = 'edit'
            title = '%s: "%s "  '%(self.model_object.model_title, obj.title)
            frm=self._get_edit_form()
        else:
            state = 'add'
            title = '%s "%s"  '%(_('Adding'),self.model_object.model_title)
            frm=self._get_add_form()            
        local_menu=self.create_menu(state, False)
        return title, frm, local_menu
        self.checkAccess(state)
    
    def _get_fields(self, obj=None):
        fields={}
        if obj:
            fields=self.obj2edit_view(obj)
            if not fields.has_key('came_from'):
                fields['came_from']=self.redirect_after('', obj)
        else:
            fields['came_from']=self.redirect_after('', obj)
        return fields
        
    def _save(self, frm, obj=None, **data):
        @validate(form=frm)
        def check(self, obj, tg_errors=None, **kw):
            return tg_errors, kw
        data_old = data.copy()
        print frm.fields
        tg_errors, data = check(self, obj, **data)
        tg_errors=tg_errors or data.get('tg_errors', None)
        fields = data
        if tg_errors:
                err = '%s %s'%('\n',tg_errors)  if isinstance(tg_errors, basestring) else ''
                turbogears.flash('%s %s'%(_("Check fields"),err))                
        else:
            request.input_values = data_old
            came_from = get_by_key_and_delete('came_from', data,self.controller_name)
            try:
                data = self.before_update(data, obj)
                if obj:
                    obj.set(**data)
                else:
                    obj = self.model_object(**data)
                turbogears.flash(_('Changes for saved!'))
                print 'save ', came_from,
                print data,
                print self.redirect_after(came_from,obj)
                self.after_update(obj, came_from=self.redirect_after(came_from,obj), **data)
            except sqlobject.dberrors.DuplicateEntryError:
                message = 'DuplicateEntryError'
                turbogears.flash('%s\n %s'%(_("Check fields"),message))   
        
    @expose()
    def edit_req(self, obj=None, **data):
        title, frm, local_menu=self._prepare_edit(obj)
        if not data:
            fields=self._get_fields(obj)
        else:
            fields = data
            self._save(frm, obj, **data)
        return dict(
                fields=fields,  
                obj=obj,
                form=frm,
                action='edit_req',
                action_text=_("Update"),
                local_menu=local_menu,
                tg_template=self.form_template,
                title = title
            )
    edit_req.expose_object = True
    add_req=edit_req
    @expose()
    def delete(self, obj):
        state = 'delete'
        
        local_menu=self.create_menu(state, False)
        self.checkAccess(state)

        delete_form = TableForm(submit_text=_("Delete anyway"),
            action='destroyCascade')

        get_ref_names = lambda refs: ["%s: %s" % (obj.model_title, obj.title) for obj in refs]

        direct_references = reversed(get_ref_names(obj.findReferences()))
        all_references = reversed(get_ref_names(obj.findReferencesCascade()))

        if len(all_references) > 0:
            return dict(
                tg_template="genshi:mnemoply._renewed_templates.crud.delete_cant",
                direct_references=direct_references,
                all_references=all_references,
                delete_form=delete_form,
            )
        else:
            title = obj.title
            obj.destroySelf()
            turbogears.flash(_('Record "%s" deleted!') % title)
            raise turbogears.redirect("../")
    delete.expose_object = True

 #   @identity.require(identity.has_any_permission('dba', 'destroyCascade'))
    @expose()
    def destroyCascade(self, obj, **data):
        obj.destroyCascade()
        title = obj.title
        turbogears.flash(_('Record "%s" deleted!') % title)
        raise turbogears.redirect("../")
    destroyCascade.expose_object = True


    def fields_for(self,sqlclass, fields=[]):
        widgetlist = []
        validatorList={}
        sqlclass_columns = database. so_columns(sqlclass)
        for field in fields:
            column = sqlclass_columns[field]
            params = self.column_parms(column)
            if field in self.widget_list:
                widget = self.widget_list[field](**params.widget)
            else:
                widget = column_widget(column, **params.widget)
            
            if widget.validator is None:
                widget.validator = EmptyStringConverter()
                
            validator = widget.validator
            validator.not_empty = params.not_empty
            if hasattr(widget, 'not_empty'):
                widget.not_empty = params.not_empty
            validator.if_missing = params.if_missing
            validator.strip = True
            validatorList[field] = validator
            widgetlist.append(widget)
        widgetlist.append(HiddenField(name='came_from'))
        return widgetlist, validatorList

    def column_parms(self,column):
        """
        Helper function to convert column attributes to
        parameters needed to initialize a widget.
        """
        d = Wrapper()
        d.not_empty = column.notNone
        d.if_missing = column.default if column.default != NoDefault else ''
        d.widget = dict(name=column.name,
                        label=column.title,
                        default = d.if_missing)
        return d
